package com.xnx3.autoPublish;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import com.xnx3.BaseVO;
import com.xnx3.DateUtil;
import com.xnx3.FileUtil;
import com.xnx3.Lang;
import com.xnx3.Log;
import com.xnx3.StringUtil;
import com.xnx3.SystemUtil;
import com.xnx3.ZipUtil;
import com.xnx3.autoPublish.SFTPUtil;
import com.xnx3.autoPublish.SSHUtil;
import com.xnx3.autoPublish.ShellBeanVO;
import com.xnx3.autoPublish.model.HttpRequestJiankangjiance;
import com.xnx3.exception.NotReturnValueException;

/**
 * 自动部署更新到服务器
 * @author 管雷鸣
 *
 */
public class AutoPublish {
	public static String SERVER_HOST = "114.115.154.xx";
	public static int SERVER_PORT = 22;
	public static String SERVER_USERNAME = "root";
	public static String SERVER_PASSWORD = "Dxxxxx";
	public static boolean PUBLISH_IGNORE_LIB_JAR = false;	//本地打包往服务器上发布，也就是往服务器上上传时，是否忽略 WEB-INF/lib/ 这个文件夹，这个文件夹下全是第三方jar，正常开发基本不会动，但文件又不较大，如果自动部署上传到服务器不包含这个的话，部署会非常快。建议如果没有jar文件改动，就设置为true
	
	/**
	 * web应用项目所在服务器的路径，注意最后没有 /
	 * 可以重新复制此处，来实现自定义web应用所在路径
	 */
	public static String WEBAPPS_PATH = "/mnt/tomcat8/webapps/ROOT"; 
	
	//服务器上要备份的文件。这里是指/mnt/tomcat8/webapps/ROOT (WEBAPPS_PATH) 下的文件。 上传项目前，先备份这里配置的文件，项目更新上去后，再把这些备份的文件还原回去
	public static List<String> SERVER_BAK_FILE;
	
	public static boolean DEBUG = false;	//debug级别日志
	
	/**
	 * 结束 root 用户 执行的所有 java 进程的命令（除了本身grep的这个之外）
	 * 如果不想执行这个，可以设置为null或者空字符串即可
	 */
	public static String SHELL_KILL_JAVA = "ps -ef | grep java | grep root | grep -v grep |awk '{print $2}' | xargs --no-run-if-empty kill -9";
	//结束tomcat
	public static String SHELL_SHUTDOWN_TOMCAT = "/mnt/tomcat8/bin/shutdown.sh";
	//启动tomcat
	public static String SHELL_START_TOMCAT = "/mnt/tomcat8/bin/startup.sh";
	
	static {
		SERVER_BAK_FILE = new ArrayList<String>();
	}
	
	public static void main(String[] args) {
		run();
	}
	
	/**
	 * 运行，执行自动部署操作
	 */
	public static void run() {
		Log.debug = false;
		
		//当前时间点，作为变量，格式为 yyyyMMdd_HHmmss
		String currentTime = DateUtil.timeForUnix10()+"";
		try {
			currentTime = DateUtil.dateFormat(DateUtil.timeForUnix10(), "yyyyMMdd_HHmmss");
		} catch (NotReturnValueException e2) {
			e2.printStackTrace();
		}
		
		/**** 项目打包 *****/
		Log.info("******* 项目打包 *****");
		String projectPath = SystemUtil.getCurrentDir();
		//获取war的名字
		String name = projectPath+File.separatorChar+"target"+File.separatorChar;
		File targetFile = new File(name);
		
		List<File> warFile = FileUtil.findSuffix(targetFile, "war");
		if(warFile.size() == 0) {
			Log.error("未发现war包存在，请先执行 mvn clean package 进行打包");
			return;
		}
		String projectName = warFile.get(0).getName().replace(".war", "");
		File compFile = new File(targetFile.getAbsolutePath()+File.separatorChar+projectName); //这个如 H:\git\yinhang_jifen_shop\target\yinhang.jifen.shop-1.0
		
		//WEB-INF/lib/ 文件
		File file = new File(compFile.getAbsolutePath()+File.separatorChar+"WEB-INF"+File.separatorChar+"lib"+File.separatorChar);
		if(PUBLISH_IGNORE_LIB_JAR) {
			//删除掉 WEB-INF/lib 文件夹，免得文件太大上传很慢
			File[] listFiles = file.listFiles();
			//递归删除目录中的jar
	        for (int i=0; i< listFiles.length; i++) {
	            listFiles[i].delete();
	        }
	        Log.info("\t删除 WEB-INF/lib/ 文件,避免安装包太大");
		}else {
			Log.info("\t不删除 WEB-INF/lib/ 文件,正常部署上传");
			
			//判断是否吧WEB-INF/lib 给删了，如果给删了，需要提示用户重新打包
			if(file == null || file.exists() == false || file.list().length == 0) {
				Log.error("您当前设置了 PUBLISH_IGNORE_LIB_JAR = false ,当前自动部署是要带 WEB-INF/lib/ 下的jar包的，请先执行 mvn clean package 重新打包项目");
				return;
			}
		}
		
        String zipFileName = "wm"+currentTime+".zip";
        //zip压缩
        try {
			ZipUtil.zip(compFile.getAbsolutePath()+File.separatorChar, compFile.getParent()+File.separatorChar, zipFileName);
        } catch (Exception e1) {
			e1.printStackTrace();
		}
        
		
		/******* 上传 *******/
        String bakPath = "/mnt/AutoPublish/"+currentTime+"/bak/";	//备份文件存放的路径
        SSHUtil ssh = new SSHUtil(SERVER_HOST, SERVER_USERNAME, SERVER_PASSWORD, SERVER_PORT); 
        ssh.open();
        //创建备份的目录，要优化，变成配置的目录，而不是写死
		ssh.exeCommand("mkdir -p "+bakPath);
		ssh.close();
		
		Log.info("******* 上传项目 *****");
		SFTPUtil sftp = new SFTPUtil();
		sftp.setHost(SERVER_HOST);
		sftp.setUsername(SERVER_USERNAME);
		sftp.setPassword(SERVER_PASSWORD);
		sftp.setPort(SERVER_PORT);
		sftp.connect();
		
		int startTime = DateUtil.timeForUnix10();
		String zipFile = targetFile.getAbsolutePath()+File.separatorChar+zipFileName;
		Log.info("\t项目正在上传到服务器，大小: "+Lang.fileSizeToInfo(new File(zipFile).length())+" 上传中...");
		BaseVO uploadVO = sftp.uploadFile("/mnt/AutoPublish/"+currentTime+"/", zipFile, zipFileName);
		if(uploadVO.getResult() - BaseVO.FAILURE == 0) {
			Log.error("项目上传到服务器时异常:"+uploadVO.getInfo());
			return;
		}
		Log.info("\t项目代码已上传到服务器，上传用时: "+(DateUtil.timeForUnix10() - startTime)+"s ,服务器中文件所在路径："+"/mnt/AutoPublish/"+currentTime+"/"+zipFileName);
		
		sftp.disconnect();
		
		

        /******* 自动备份服务器相关文件 *******/
        Log.info("******* 自动备份服务器相关文件 *****");
		ssh.open();
		
		//创建目录,自动备份配置文件
		for (int i = 0; i < SERVER_BAK_FILE.size(); i++) {
			String path = SERVER_BAK_FILE.get(i);
			
			//判断服务器上是否有这个文件
			ShellBeanVO shellVO = ssh.exeCommand("ls "+WEBAPPS_PATH+SERVER_BAK_FILE.get(i));
			if(shellVO.getResult() - BaseVO.FAILURE == 0) {
				Log.error("\t["+SERVER_BAK_FILE.get(i)+"] 服务器中，此文件("+WEBAPPS_PATH+SERVER_BAK_FILE.get(i)+")不存在，请检查 SERVER_BAK_FILE 设置项");
				return;
			}
			
			//判断是不是目录
			if((path.lastIndexOf("/")+1) - path.length() == 0) {
				//当前SERVER_BAK_FILE 配置的是个目录，如 /WEB-INF/view/ 那当前是暂时不支持目录的，待下次更新加入
				Log.error("当前SERVER_BAK_FILE ("+path+") 配置的是个目录，如 /WEB-INF/view/ 那当前是暂时不支持目录的，待下次更新加入");
				return;
			}
			
			if(path.split("/").length > 2) {
				//有上级目录，取上级目录,进行建立目录
				String mulu = StringUtil.subString(path, null, "/", 1);  //这个取出来的格式如  WEB-INF/classes
				ShellBeanVO muluVO = ssh.exeCommand("mkdir -p /mnt/AutoPublish/"+currentTime+"/bak/"+mulu);
				if(muluVO.getResult() - ShellBeanVO.FAILURE == 0) {
					Log.error("创建目录失败："+muluVO.getInfo()+", 执行的命令："+"mkdir -p /mnt/AutoPublish/"+currentTime+"/bak/"+mulu);
					return;
				}
			}else {
				//根目录下
			}
			
			//要优化，变成配置的目录，而不是写死
//			ssh.exeCommand("mkdir -p /mnt/AutoPublish/"+currentTime+"/bak/WEB-INF/classes/");
			ssh.exeCommand("cp "+WEBAPPS_PATH+SERVER_BAK_FILE.get(i)+" /mnt/AutoPublish/"+currentTime+"/bak"+SERVER_BAK_FILE.get(i));
			Log.info("\t已将 ["+SERVER_BAK_FILE.get(i)+"] 备份至 /mnt/AutoPublish/"+currentTime+"/bak/"+SERVER_BAK_FILE.get(i));
		}

		if(PUBLISH_IGNORE_LIB_JAR) {
			//备份jar
			ssh.exeCommand("mkdir -p /mnt/AutoPublish/"+currentTime+"/bak/WEB-INF/lib/");
			ssh.exeCommand("cp -r "+WEBAPPS_PATH+"/WEB-INF/lib /mnt/AutoPublish/"+currentTime+"/bak/WEB-INF/");
			Log.info("\t已将 [WEB-INF/lib/] 备份至 /mnt/AutoPublish/"+currentTime+"/bak/WEB-INF/lib/");
		}
		
    	/***** 服务器重新部署 *******/
    	//结束tomcat运行
		Log.info("******* 服务器重新部署 *****");
    	ShellBeanVO shutTomcatVO = ssh.exeCommand(SHELL_SHUTDOWN_TOMCAT);
    	if(shutTomcatVO.getResult() - BaseVO.SUCCESS == 0) {
    		Log.info("\t已执行命令正在结束tomcat运行");
    	}
    	try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
    	
    	if(SHELL_KILL_JAVA != null && SHELL_KILL_JAVA.length() > 0) {
    		ShellBeanVO kill_java_vo = ssh.exeCommand(SHELL_KILL_JAVA);
        	if(kill_java_vo.getResult() - BaseVO.SUCCESS == 0) {
        		Log.info("\t避免没结束完全，已执行kill -9 强制检查一下");
        	}
    	}
    	
//    	try {
//    		Log.info("延迟等2秒");
//			Thread.sleep(2000);
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
    	
    	// yum 安装 unzip 命令，废弃，因为再前提条件服务器环境中已经安装了unzip
//    	ShellBeanVO yumUnzip = ssh.exeCommand("yum -y install unzip");
//		if(yumUnzip.getResult() - BaseVO.FAILURE == 0) {
//			Log.error("出错，把上传的项目包转移到ROOT下时，执行命令 yum -y install unzip 时异常："+yumUnzip.getInfo());
//			return;
//		}
//		try {
//			Thread.sleep(5000);
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
    	
    	//清空 ROOT 目录
		ssh.exeCommand("rm -rf "+WEBAPPS_PATH+"/*");
		Log.info("\t清空 ROOT 目录");
		//把上传的项目包转移到ROOT下
		
		ssh.exeCommand("cp /mnt/AutoPublish/"+currentTime+"/"+zipFileName+" "+WEBAPPS_PATH+"/");
		Log.info("\t把上传的项目包转移到ROOT下");
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//解压，并删除项目zip包
		ShellBeanVO unzipVO = ssh.exeCommand("unzip -d "+WEBAPPS_PATH+"/ "+WEBAPPS_PATH+"/"+zipFileName);
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		ssh.exeCommand("rm -rf "+WEBAPPS_PATH+"/"+zipFileName);
		
		//将备份的进行还原
		for (int i = 0; i < SERVER_BAK_FILE.size(); i++) {
			String path = SERVER_BAK_FILE.get(i);
			//先删除上传的项目本身的
			ssh.exeCommand("rm -rf "+WEBAPPS_PATH+path);
			
			String mulu = "/";
			if(path.split("/").length > 2) {
				//有上级目录
				mulu = StringUtil.subString(path, null, "/", 1);  //这个取出来的格式如  WEB-INF/classes
			}else {
				//根目录下
			}
			ssh.exeCommand("cp -rf /mnt/AutoPublish/"+currentTime+"/bak"+path+" "+WEBAPPS_PATH+mulu);
			Log.info("\t已将 ["+SERVER_BAK_FILE.get(i)+"] 还原至 "+WEBAPPS_PATH+path);
		}
		
//		System.out.println("cp -rf /mnt/AutoPublish/"+currentTime+"/bak/WEB-INF/classes/application.properties /mnt/tomcat8/webapps/ROOT/WEB-INF/classes/");
		//ssh.exeCommand("cp -rf /mnt/AutoPublish/"+currentTime+"/bak/WEB-INF/classes/application.properties /mnt/tomcat8/webapps/ROOT/WEB-INF/classes/");
		if(PUBLISH_IGNORE_LIB_JAR) {
			ssh.exeCommand("cp -rf /mnt/AutoPublish/"+currentTime+"/bak/WEB-INF/lib/ "+WEBAPPS_PATH+"/WEB-INF/");
			Log.info("\t已将 [/WEB-INF/lib/] 还原至 "+WEBAPPS_PATH+"/WEB-INF/");
		}
		Log.info("\t将备份的进行还原");
		
    	//运行tomcat
    	ShellBeanVO startupTomcatVO = ssh.exeCommand(SHELL_START_TOMCAT);
    	if(startupTomcatVO.getResult() - BaseVO.SUCCESS == 0) {
    		Log.info("\t已启动tomcat...");
    	}
    	
        ssh.close();
        
        Log.info("******* 健康监测-检测是否正常启动 *****");
        String url = "http://"+SERVER_HOST;
        int jiankangStartTime = DateUtil.timeForUnix10();	//记录开始时间
        while(true) {
        	try {
        		BaseVO requestVO = HttpRequestJiankangjiance.request(url);
        		if(requestVO.getResult() - BaseVO.SUCCESS == 0) {
            		Log.info("\t监测完毕，项目已健康启动！");
            		break;
            	}else {
            		int haoshi = DateUtil.timeForUnix10() - jiankangStartTime;	//记录开始健康检查到现在的耗时过了多久了
            		if(haoshi < 120) {
            			//120秒内如果还未启动，持续打印
            			Log.info("\t["+haoshi+"s] 还未正常启动，继续检测中...  ("+requestVO.getInfo()+")");
            		}else {
            			Log.error("\t["+haoshi+"s]已经超过两分钟还没能正常启动，请登录服务器查看日志文件 /mnt/tomcat8/log/catalina.out 进行检查");
            			break;
            		}
            	}
			} catch (Exception e) {
			}
        	try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
        }
        
        System.exit(0);
	}
	
	
}
